﻿#include "curlDownload.h"
#include <mutex>
#include <iostream>
#include <fstream>
#include <curl/curl.h>
#include <chrono>
#include <io.h>
#include <direct.h>
#include <thread/threadPool.h>

// 定义全局变量
namespace HNNetwork {

    //std::mutex downloadMtx;

    // 回调函数用于获取头部信息
  /*  size_t getHeaderSizeCallback(void* ptr, size_t size, size_t nmemb, void* stream)
    {
        char* header_line = (char*)ptr;
        size_t len = size * nmemb;

        // 查找Content-Length字段
        if (strncmp(header_line, "Content-Length:", 15) == 0) {
            long file_size = strtol(header_line + 15, NULL, 10);
        }

        return len;
    }
    */
    bool getDownloadSize(std::vector<UrlObeject*>& urlObjects, long& sum)
    {
        CURL* curl = curl_easy_init();
        if (!curl)
        {
            std::cerr << "Failed to initialize libcurl." << std::endl;
            return false;
        }
        std::vector<UrlObeject*>::iterator iter = urlObjects.begin();
        for (; iter != urlObjects.end(); ++iter)
        {
            CURLcode res = curl_easy_setopt(curl, CURLOPT_URL, (*iter)->url);
            ASSERT_CURLCODE_CONTINUE(curl, (*iter)->url, res);
            // 不下载内容
            res = curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
            ASSERT_CURLCODE_CONTINUE(curl, (*iter)->url, res);
            // 只获取头部信息
            res = curl_easy_setopt(curl, CURLOPT_HEADER, 1L);
            ASSERT_CURLCODE_CONTINUE(curl, (*iter)->url, res);
#ifdef  ENABLE_OPEN_SSL  // 设置SSL选项
            // 验证服务器的SSL证书
            res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
            ASSERT_CURLCODE_CONTINUE(curl, (*iter)->url, res);
            // 验证证书上的主机名
            res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 2L);
            ASSERT_CURLCODE_CONTINUE(curl, (*iter)->url, res);

#endif //  ENABLE_OPEN_SSL
            //res = curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, getHeaderSizeCallback);
            //ASSERT_CURLCODE_CONTINUE(curl, (*iter)->url, res);
            //res = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
           // ASSERT_CURLCODE_CONTINUE(curl, (*iter)->url, res);
            //res = curl_easy_setopt(curl, CURLOPT_WRITEHEADER, NULL);
            //ASSERT_CURLCODE_CONTINUE(curl, (*iter)->url, res);
            // 获取文件大小
            res = curl_easy_perform(curl);
            ASSERT_CURLCODE_CONTINUE(curl, (*iter)->url, res);
            double file_size = 0.0;
            res = curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &file_size);
            ASSERT_CURLCODE_CONTINUE(curl, (*iter)->url, res);
            (*iter)->size = static_cast<long>(file_size);
            sum += (*iter)->size;
        }
        curl_easy_cleanup(curl);
        return true;
    }

    void downloadRange(int threadIdx,long offset, long blocksize, const char* url, std::ofstream& tempfile)
    {
        //每个线程初始化一个句柄，绝对不能混用
        CURL* curl = curl_easy_init();
        if (!curl)
        {
            std::cerr << "Failed to initialize libcurl." << std::endl;
            return;
        }
        
        // 设置请求头，指定下载范围
        std::string rangeHeader = std::to_string(offset) + "-" + std::to_string(offset + blocksize-1);
        //std::string rangeHeader = "Range: bytes=" + std::to_string(start) + "-" + std::to_string(end);
        struct curl_slist* headers = nullptr;
        headers = curl_slist_append(headers, rangeHeader.c_str());

        // 设置相关选项并执行下载, /* 如果断点续传，设置 Range 头部信息 */
        CURLcode res = curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
        ASSERT_CURLCODE(curl, url, res);
        res = curl_easy_setopt(curl, CURLOPT_URL, url);
        ASSERT_CURLCODE(curl, url, res);
        res = curl_easy_setopt(curl, CURLOPT_RANGE, rangeHeader.c_str());
        ASSERT_CURLCODE(curl, url, res);

#ifdef ENABLE_OPEN_SSL
        // 使用OpenSSL进行HTTPS下载
        res = curl_easy_setopt(curl, CURLOPT_SSLENGINE, "openssl");
        ASSERT_CURLCODE(curl, url, res);

        res = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
        ASSERT_CURLCODE(curl, url, res);
#endif
        res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
        ASSERT_CURLCODE(curl, url, res);

        res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &tempfile);
        ASSERT_CURLCODE(curl, url, res);
#ifdef ENABLE_OPEN_SSL
        // 创建自定义OpenSSL输出BIO
        BIO_METHOD* bioMethod = BIO_meth_new(BIO_get_ssl_method(SSLv23_client_method()));
        BIO* sslBio = BIO_new(bioMethod);
        SSL_CTX* sslContext = SSL_CTX_new(SSLv23_client_method());
        SSL_CTX_set_verify(sslContext, SSL_VERIFY_NONE, nullptr);
        SSL* ssl = SSL_new(sslContext);
        SSL_set_bio(ssl, sslBio, sslBio);
        BIO_set_ssl(sslBio, ssl, BIO_CLOSE);

        // 将自定义输出BIO附加到回调文件
        BIO_set_data(sslBio, &tempfile);
        BIO_set_write_callback(sslBio, opensslBioWrite);
        // 将自定义OpenSSL BIO设置为libcurl的SSL_CTX_FUNCTION和SSL_CTX_DATA选项
        res = curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, nullptr);
        ASSERT_CURLCODE(curl, url, res);

        res = curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, sslContext);
        ASSERT_CURLCODE(curl, url, res);

#endif
        /* 执行 HTTP 请求 */
        res = curl_easy_perform(curl);
        ASSERT_CURLCODE(curl, url, res);

#ifdef ENABLE_OPEN_SSL
        SSL_CTX_free(sslContext);
#endif

        tempfile.close();
        // 清理资源
        curl_slist_free_all(headers);
        curl_easy_cleanup(curl);
        std::cout << "第 " << threadIdx << " 个线程,ID:" << std::this_thread::get_id() << ",退出！" << url << ",offset:" << offset << ",blocksize:" << blocksize << std::endl;
    }

    void downloadFile(UrlObeject* urlObj, long flowSize)
    {
        CURL* curl = curl_easy_init();
        if (!curl)
        {
            std::cerr << "Failed to initialize libcurl." << std::endl;
            return;
        }
        std::string tmpFileDir = "./"+ urlObj ->uuid+"/";
        if (_access(tmpFileDir.c_str(), 0) == -1) //如果文件夹不存在
        {
            int flag = _mkdir(tmpFileDir.c_str()); //则创建
            if (flag != 0)
            {
                std::cout << "临时文件夹" << tmpFileDir.c_str() << "创建失败，code: " << flag << std::endl;
                return;
            }
        }

        CURLcode res = curl_easy_setopt(curl, CURLOPT_URL, urlObj->url);
        ASSERT_CURLCODE(curl, urlObj->url, res);
        long threadCount = getThreadCount(urlObj->size, flowSize);
       // std::vector<std::thread> threads;// (threadCount);
        ThreadPool  pool(MAXTHREADCOUNT);
        std::vector< std::future<void> > results;
        std::vector<std::ofstream> fileParts(threadCount);
        //long partSize = std::floor( urlObj->size / (float)threadCount);
       // std::string baseTmpFilePath = tmpFileDir + std::string("/")+urlObj->filePath + std::string(".part");
        //long lastSize = (partSize + urlObj->size % threadCount);
       // threadCount = 50;
        std::string baseTmpFilePath = tmpFileDir + urlObj->filePath + std::string(".part");
        long maxsize = urlObj->size;// 501;
        long partSize = maxsize / threadCount;
        long lastSize = (partSize + maxsize % threadCount);

        std::cout << "总计切片：" << threadCount <<" 个，每个切片 "<< partSize << " bits" << std::endl;
        for (long i = 0; i < threadCount; ++i)
        {
            std::string tmpFile = baseTmpFilePath + std::to_string(i);
            fileParts[i].open(tmpFile.c_str(), std::ofstream::binary);
            if (fileParts[i].is_open())
            {
                long blocksize = (i == (threadCount - 1)) ? lastSize : partSize;
                results.emplace_back(pool.enqueue(downloadRange, i,(i * partSize), blocksize, urlObj->url, std::ref(fileParts[i])));
               // threads.emplace_back(downloadRange, (i * partSize), blocksize, urlObj->url, std::ref(fileParts[i]));
            }
            else
            {
                std::cout << "缓存文件打开失败，" << tmpFile.c_str() << std::endl;
            }
        }

        pool.wait();
        curl_easy_cleanup(curl);
        std::ofstream output(urlObj->filePath, std::ofstream::binary);
        if (!output)
        {
            std::cout << "本地文件打开失败，" << urlObj->filePath << std::endl;
        }
        for (size_t i = 0; i < threadCount;i++)
        {
            std::string tmpFile = baseTmpFilePath + std::to_string(i);
            std::ifstream input(tmpFile.c_str(), std::ifstream::binary);
            if (!input)
            {
                std::cout << "本地缓存文件打开失败，" << tmpFile.c_str() << std::endl;
                continue;
            }
            output << input.rdbuf();
            input.close();
           // input.clear();
            
            int flag = remove(tmpFile.c_str());
            if (flag != 0)
            {
                std::cout << "本地缓存文件删除失败，" << tmpFile.c_str() << " code:" << flag << std::endl;
            }
        }

        int flag = _rmdir(tmpFileDir.c_str());
        if (flag != 0)
        {
            std::cout << "本地缓存文件夹删除失败，" << tmpFileDir.c_str() << " code:" << flag << std::endl;
        }
        output.close();
        //output.clear();
    }
    // 下载指定范围的数据块
    //void DownloadRange(const std::string& url, const std::string& filepath, long start, long end,std::vector<std::ofstream> &tempfiles)
    //{
    //    CURL* curl = curl_easy_init();
    //    if (!curl)
    //    {
    //        std::cerr << "Failed to initialize libcurl." << std::endl;
    //               return;
    //    }
    //        std::ofstream file(filepath, std::ios_base::out | std::ios_base::binary | std::ios_base::app);
    //        if (!file) {
    //            curl_easy_cleanup(curl);
    //            return;
    //        }
    //        tempfiles.push_back(std::move(file));

    //        // 设置请求头，指定下载范围
    //        std::string rangeHeader = "Range: bytes=" + std::to_string(start) + "-" + std::to_string(end);
    //        struct curl_slist* headers = nullptr;
    //        headers = curl_slist_append(headers, rangeHeader.c_str());

    //        // 设置相关选项并执行下载
    //        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
    //        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
    //        curl_easy_setopt(curl, CURLOPT_WRITEDATA, &tempfiles.back());
    //        curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
    //        CURLcode res = curl_easy_perform(curl);
    //        // 清理资源
    //        curl_slist_free_all(headers);
    //        curl_easy_cleanup(curl);
    //        if (res != CURLE_OK) {
    //            downloadMtx.lock();
    //            MessageCallback(url,res,curl_easy_strerror(res));
    //            downloadMtx.unlock();
    //        }

    //}
    //void downloadFile(const std::vector<UrlObeject*> &urlObjects)
    //{
    //    CURL* curl = curl_easy_init();
    //        if (!curl)
    //        {
    //            std::cerr << "Failed to initialize libcurl." << std::endl;
    //            return;
    //        }
    //        std::vector<std::ofstream> tempfiles;  // 存储已下载的文件
    //        // 启动多线程下载
    //        std::vector<std::thread> threads;
    //        std::vector<UrlObeject*>::const_iterator iter = urlObjects.begin();
    //        for(;iter != urlObjects.end();++iter)
    //        {
    //            UrlObeject* obj = *iter;
    //            long start = 0;
    //            long end = 0;
    //            const char *tmpPath = (std::string(obj->url)+".tmp").c_str();
    //            // 获取已下载文件的大小，用于设置断点续传范围
    //            std::ifstream file(tmpPath, std::ios_base::in | std::ios_base::binary);
    //            if (file) {
    //                file.seekg(0, std::ios_base::end);
    //                start = static_cast<long>(file.tellg());
    //                file.close();

    //                tempfiles.emplace_back(tmpPath, std::ios_base::out | std::ios_base::binary | std::ios_base::app);
    //                end = -1;  // 设置为-1表示从start位置一直下载到文件末尾
    //            }
    //            threads.emplace_back(DownloadRange, obj->url, tmpPath, start, end,tempfiles);
    //        }


    //    // 等待所有下载线程结束
    //    for (auto& thread : threads) {
    //        if (thread.joinable()) {
    //            thread.join();
    //        }
    //    }
    //    // 合并临时文件为最终文件
    //    for (size_t i = 0; i < tempfiles.size(); ++i) {
    //        const char *tmpPath = (std::string(urlObjects[i]->url)+".tmp").c_str();
    //        std::ifstream tmpFile(tmpPath, std::ios_base::in | std::ios_base::binary);
    //        std::ofstream finalFile(urlObjects[i]->url, std::ios_base::out | std::ios_base::binary);
    //        if (tmpFile && finalFile)
    //        {
    //            finalFile << tmpFile.rdbuf();
    //        }
    //        tmpFile.close();
    //        finalFile.close();
    //        // 删除临时文件
    //        std::remove(tmpPath);
    //    }
    //}

    //void downloadFile(const std::vector<std::string> &urls)
    //{
    //    std::vector<std::ofstream> tempfiles;  // 存储已下载的文件
    //    // 启动多线程下载
    //    std::vector<std::thread> threads;
    //    for (size_t i = 0; i < urls.size(); ++i) {
    //        long start = 0;
    //        long end = 0;
    //        // 获取已下载文件的大小，用于设置断点续传范围
    //        std::ifstream file(urls[i] + ".tmp", std::ios_base::in | std::ios_base::binary);
    //        if (file) {
    //            file.seekg(0, std::ios_base::end);
    //            start = static_cast<long>(file.tellg());
    //            file.close();

    //            tempfiles.emplace_back(urls[i] + ".tmp", std::ios_base::out | std::ios_base::binary | std::ios_base::app);
    //            end = -1;  // 设置为-1表示从start位置一直下载到文件末尾
    //        }
    //        threads.emplace_back(DownloadRange, urls[i], urls[i] + ".tmp", start, end,tempfiles);
    //    }
    //    // 等待所有下载线程结束
    //    for (auto& thread : threads) {
    //        if (thread.joinable()) {
    //            thread.join();
    //        }
    //    }
    //    // 合并临时文件为最终文件
    //    for (size_t i = 0; i < tempfiles.size(); ++i) {
    //        std::ifstream tmpFile(urls[i] + ".tmp", std::ios_base::in | std::ios_base::binary);
    //        std::ofstream finalFile(urls[i], std::ios_base::out | std::ios_base::binary);

    //        if (tmpFile && finalFile) {
    //            finalFile << tmpFile.rdbuf();
    //        }
    //        tmpFile.close();
    //        finalFile.close();
    //        // 删除临时文件
    //        std::remove((urls[i] + ".tmp").c_str());
    //    }
    //}
}//HNNetWork
